package com.yphone.utils;

import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.io.Reader;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class GsonUtils {
    private static final Map<String, Gson> GSONS = new HashMap();
    private static final String KEY_DEFAULT = "defaultGson";
    private static final String KEY_DELEGATE = "delegateGson";
    private static final String KEY_LOG_UTILS = "logUtilsGson";

    private GsonUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    public static void setGsonDelegate(Gson delegate) {
        if (delegate != null) {
            GSONS.put(KEY_DELEGATE, delegate);
        }
    }

    public static void setGson(String key, Gson gson) {
        if (!TextUtils.isEmpty(key) && gson != null) {
            GSONS.put(key, gson);
        }
    }

    public static Gson getGson(String key) {
        return (Gson) GSONS.get(key);
    }

    public static Gson getGson() {
        Gson gsonDelegate = (Gson) GSONS.get(KEY_DELEGATE);
        if (gsonDelegate != null) {
            return gsonDelegate;
        }
        Gson gsonDefault = (Gson) GSONS.get(KEY_DEFAULT);
        if (gsonDefault == null) {
            gsonDefault = createGson();
            GSONS.put(KEY_DEFAULT, gsonDefault);
        }
        return gsonDefault;
    }

    public static String toJson(@NonNull Object object) {
        if (object != null) {
            return toJson(getGson(), object);
        }
        throw new NullPointerException("Argument 'object' of type Object (#0 out of 1, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
    }

    public static String toJson(@NonNull Object src, @NonNull Type typeOfSrc) {
        if (src == null) {
            throw new NullPointerException("Argument 'src' of type Object (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (typeOfSrc != null) {
            return toJson(getGson(), src, typeOfSrc);
        } else {
            throw new NullPointerException("Argument 'typeOfSrc' of type Type (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static String toJson(@NonNull Gson gson, @NonNull Object object) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (object != null) {
            return gson.toJson(object);
        } else {
            throw new NullPointerException("Argument 'object' of type Object (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static String toJson(@NonNull Gson gson, @NonNull Object src, @NonNull Type typeOfSrc) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (src == null) {
            throw new NullPointerException("Argument 'src' of type Object (#1 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (typeOfSrc != null) {
            return gson.toJson(src, typeOfSrc);
        } else {
            throw new NullPointerException("Argument 'typeOfSrc' of type Type (#2 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull String json, @NonNull Class<T> type) {
        if (json == null) {
            throw new NullPointerException("Argument 'json' of type String (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return fromJson(getGson(), json, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Class<T> (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull String json, @NonNull Type type) {
        if (json == null) {
            throw new NullPointerException("Argument 'json' of type String (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return fromJson(getGson(), json, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Type (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Reader reader, @NonNull Class<T> type) {
        if (reader == null) {
            throw new NullPointerException("Argument 'reader' of type Reader (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return fromJson(getGson(), reader, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Class<T> (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Reader reader, @NonNull Type type) {
        if (reader == null) {
            throw new NullPointerException("Argument 'reader' of type Reader (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return fromJson(getGson(), reader, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Type (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Gson gson, @NonNull String json, @NonNull Class<T> type) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (json == null) {
            throw new NullPointerException("Argument 'json' of type String (#1 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return gson.fromJson(json, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Class<T> (#2 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Gson gson, @NonNull String json, @NonNull Type type) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (json == null) {
            throw new NullPointerException("Argument 'json' of type String (#1 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return gson.fromJson(json, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Type (#2 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Gson gson, @NonNull Reader reader, @NonNull Class<T> type) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (reader == null) {
            throw new NullPointerException("Argument 'reader' of type Reader (#1 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return gson.fromJson(reader, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Class<T> (#2 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }

    public static <T> T fromJson(@NonNull Gson gson, @NonNull Reader reader, @NonNull Type type) {
        if (gson == null) {
            throw new NullPointerException("Argument 'gson' of type Gson (#0 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (reader == null) {
            throw new NullPointerException("Argument 'reader' of type Reader (#1 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        } else if (type != null) {
            return gson.fromJson(reader, type);
        } else {
            throw new NullPointerException("Argument 'type' of type Type (#2 out of 3, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
        }
    }
//
//    public static Type getListType(@NonNull Type type) {
//        if (type == null) {
//            throw new NullPointerException("Argument 'type' of type Type (#0 out of 1, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        }
//        return TypeToken.getParameterized(List.class, type).getType();
//    }
//
//    public static Type getSetType(@NonNull Type type) {
//        if (type == null) {
//            throw new NullPointerException("Argument 'type' of type Type (#0 out of 1, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        }
//        return TypeToken.getParameterized(Set.class, type).getType();
//    }
//
//    public static Type getMapType(@NonNull Type keyType, @NonNull Type valueType) {
//        if (keyType == null) {
//            throw new NullPointerException("Argument 'keyType' of type Type (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        } else if (valueType == null) {
//            throw new NullPointerException("Argument 'valueType' of type Type (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        } else {
//            return TypeToken.getParameterized(Map.class, keyType, valueType).getType();
//        }
//    }
//
//    public static Type getArrayType(@NonNull Type type) {
//        if (type != null) {
//            return TypeToken.getArray(type).getType();
//        }
//        throw new NullPointerException("Argument 'type' of type Type (#0 out of 1, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//    }
//
//    public static Type getType(@NonNull Type rawType, @NonNull Type... typeArguments) {
//        if (rawType == null) {
//            throw new NullPointerException("Argument 'rawType' of type Type (#0 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        } else if (typeArguments != null) {
//            return TypeToken.getParameterized(rawType, typeArguments).getType();
//        } else {
//            throw new NullPointerException("Argument 'typeArguments' of type Type[] (#1 out of 2, zero-based) is marked by @android.support.annotation.NonNull but got null for it");
//        }
//    }

    static Gson getGson4LogUtils() {
        Gson gson4LogUtils = (Gson) GSONS.get(KEY_LOG_UTILS);
        if (gson4LogUtils != null) {
            return gson4LogUtils;
        }
        Gson gson4LogUtils2 = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
        GSONS.put(KEY_LOG_UTILS, gson4LogUtils2);
        return gson4LogUtils2;
    }

    private static Gson createGson() {
        return new GsonBuilder().serializeNulls().disableHtmlEscaping().create();
    }
}